package cc.rengu.igas.channel.wechat.core.service.trans;


import cc.rengu.igas.channel.wechat.common.constant.WechatConfigConstant;
import cc.rengu.igas.channel.wechat.common.constant.WechatParamConstant;
import cc.rengu.igas.channel.wechat.common.constant.WechatTreeNodeConstant;
import cc.rengu.igas.channel.wechat.common.enums.RespCodeEnum;
import cc.rengu.igas.channel.wechat.core.model.ChnlBizResponse;
import cc.rengu.igas.channel.wechat.core.realize.WechatSignService;
import cc.rengu.igas.channel.wechat.core.realize.impl.WechatSignServiceImpl;
import cc.rengu.igas.channel.wechat.core.service.base.PayService;
import cc.rengu.igas.channel.wechat.facade.bean.IdentificationInfo;
import cc.rengu.igas.channel.wechat.facade.bean.LicenceInfo;
import cc.rengu.igas.channel.wechat.facade.bean.SettleAcctInfo;
import cc.rengu.igas.channel.wechat.facade.request.WechatRiskMchntQueryRequest;
import cc.rengu.igas.channel.wechat.facade.response.WechatRiskMchntQueryResponse;
import cc.rengu.oltp.service.common.constant.AppParamConstant;
import cc.rengu.oltp.service.common.constant.TreeNodeConstant;
import cc.rengu.oltp.service.common.dao.CertInfoMapper;
import cc.rengu.oltp.service.common.dao.SecPlanInfoMapper;
import cc.rengu.oltp.service.common.dao.impl.CertInfoMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.SecPlanInfoMapperImpl;
import cc.rengu.oltp.service.common.entity.*;
import cc.rengu.oltp.service.model.BizException;
import cc.rengu.oltp.service.realize.SysParamService;
import cc.rengu.oltp.service.realize.impl.SysParamServiceImpl;
import cc.rengu.oltp.utility.util.ConvertUtil;
import cc.rengu.oltp.utility.util.DateUtil;
import cc.rengu.oltp.utility.util.XmlTreeUtil;
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.asn1.pkcs.RSAPublicKey;


import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.RSAPublicKeySpec;
import java.util.*;


/**
 * 微信风险商户信息查询
 */
public class RiskMchntQueryService extends PayService {

    @Override
    protected Object beforeBizProcess() throws Exception {
        WechatRiskMchntQueryRequest riskMchntQueryRequest = new WechatRiskMchntQueryRequest();
        ConvertUtil.convertOutput(riskMchntQueryRequest);
        return riskMchntQueryRequest;
    }

    @Override
    protected ChnlBizResponse callBizService(Object request) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        int iReturnCode = 0;

        WechatRiskMchntQueryResponse riskMchntQueryResponse = new WechatRiskMchntQueryResponse();
        WechatRiskMchntQueryRequest riskMchntQueryRequest = (WechatRiskMchntQueryRequest) request;

        DstChannelCfg dstChannelCfg = (DstChannelCfg) xmlTreeUtil.getXmlTreeObjectValue(WechatTreeNodeConstant.DST_CHANNEL_CFG, DstChannelCfg.class);
        DstChannelInfo dstChannelInfo = (DstChannelInfo) xmlTreeUtil.getXmlTreeObjectValue(WechatTreeNodeConstant.DST_CHANNEL_INFO, DstChannelInfo.class);
        ConvertUtil.convertInput(riskMchntQueryRequest);
        xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.WX_CHANNEL_NO, dstChannelCfg.getChannelNo());
        xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.WX_MCHNT_NO, dstChannelCfg.getProviderMchntNo());
        xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.WX_APP_ID, dstChannelCfg.getAppId());
        encSensitiveInfo(riskMchntQueryRequest, dstChannelCfg);
        xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.TXN_NUM, riskMchntQueryRequest.getHeader().getTxnNum());
        xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.WX_SIGN_TYPE, WechatParamConstant.HMAC_SHA256_SIGNTYPE);
        SysParamService sysParamService = new SysParamServiceImpl();
        SysParam sysParam = sysParamService.getSysParamInfo(AppParamConstant.DEFAULT_INSTID,
                WechatConfigConstant.WECHAT_CONF_NODE, WechatConfigConstant.WECHAT_RISK_QUERY_URL);
        if (sysParam == null) {
            rglog.error("获取微信风险商户查询url失败，请配置");
            throw new BizException(RespCodeEnum.CONFIG_PARAM_NOT_EXIST.getRespCode(), RespCodeEnum.CONFIG_PARAM_NOT_EXIST.getRespDesc());
        }
        xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.DEFINE_HTTP_URL_NODE, sysParam.getParamValue().replace("{merId}", dstChannelCfg.getProviderMchntNo()));
        iReturnCode = schedule.callService(dstChannelInfo.getDstServiceName(), 0);
        if (xmlTreeUtil.isExist(WechatTreeNodeConstant.DEFINE_HTTP_URL_NODE)) {
            xmlTreeUtil.deleteXmlTreeNode(WechatTreeNodeConstant.DEFINE_HTTP_URL_NODE);
        }
        if (0 != iReturnCode && 98 != iReturnCode) {
            rglog.error("调用微信服<{}>失败，iReturnCode:<{}>", dstChannelInfo.getDstServiceName(), iReturnCode);
            throw new BizException(RespCodeEnum.CALL_BIZ_SERVICE_ERROR.getRespCode(), RespCodeEnum.CALL_BIZ_SERVICE_ERROR.getRespDesc());
        } else if (98 == iReturnCode) {
            rglog.error("调用微信服<{}>超时，iReturnCode:<{}>", dstChannelInfo.getDstServiceName(), iReturnCode);
            throw new BizException(RespCodeEnum.TIME_OUT_ERROR.getRespCode(), RespCodeEnum.TIME_OUT_ERROR.getRespDesc());
        }
        ConvertUtil.convertOutput(riskMchntQueryResponse);
        String returnCode = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.WX_RETURN_CODE);
        String returnMsg = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.WX_RETURN_MSG);
        if (!WechatParamConstant.STRING_SUCCESS.equals(returnCode)) {
            riskMchntQueryResponse.setRespCode(returnCode);
            riskMchntQueryResponse.setRespDesc(returnMsg);
            rglog.error("调用第三方服务失败,respCode=[{}],respDesc=[{}]", riskMchntQueryResponse.getRespCode(), riskMchntQueryResponse.getRespDesc());
        } else {
            riskMchntQueryResponse.setRespCode(RespCodeEnum.TRANS_SUCCESS.getRespCode());
            riskMchntQueryResponse.setRespDesc(RespCodeEnum.TRANS_SUCCESS.getRespDesc());
        }
        ChnlBizResponse chnlBizResponse = new ChnlBizResponse();
        chnlBizResponse.setResult(riskMchntQueryResponse);
        return chnlBizResponse;
    }

    @Override
    protected void afterBizProcess(ChnlBizResponse response) throws Exception {
        WechatRiskMchntQueryResponse riskMchntQueryResponse = (WechatRiskMchntQueryResponse) response.getResult();
        ConvertUtil.convertInput(riskMchntQueryResponse);
    }

    /**
     * 加密敏感信息
     *
     * @param riskMchntQueryRequest
     * @param dstChannelCfg
     * @throws Exception
     */
    private void encSensitiveInfo(WechatRiskMchntQueryRequest riskMchntQueryRequest, DstChannelCfg dstChannelCfg) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String instId = riskMchntQueryRequest.getHeader().getInstId();
        if (riskMchntQueryRequest.getIdentificationInfo() == null &&
                riskMchntQueryRequest.getLicenceInfo() == null &&
                riskMchntQueryRequest.getSettleAcctInfo() == null) {
            return;
        }
        CertInfo certInfo = null;
        /* 根据通道安全计划获取安全计划 */
        SecPlanInfoMapper secPlanInfoMapper = new SecPlanInfoMapperImpl();
        List<SecPlanInfo> secPlanInfoList = secPlanInfoMapper.selectSecPlanInfoBySecPlanId(instId, dstChannelCfg.getSecPlanId());
        if (null != secPlanInfoList && !secPlanInfoList.isEmpty()) {
            Optional<SecPlanInfo> secPlanInfoOpt = secPlanInfoList.stream().filter(item -> item.getSecPlanType().equals(WechatParamConstant.ENCRYPT_CERT_TYPE)).findFirst();
            if (secPlanInfoOpt.isPresent()) {
                CertInfoMapper certInfoMapper = new CertInfoMapperImpl();
                certInfo = certInfoMapper.selectCertInfoByPrimaryKey(secPlanInfoOpt.get().getInstId() + secPlanInfoOpt.get().getSecPlanId() + secPlanInfoOpt.get().getSecPlanType());
            }
        }
        if (certInfo == null) {
            rglog.error("微信服务商[{}]未配置加密公钥", riskMchntQueryRequest.getChannelType());
            /*实时获取加密的RSA公钥*/
            certInfo = getRSAPubKey(secPlanInfoList.get(0));
            if (certInfo == null) {
                throw new BizException(RespCodeEnum.PUB_CERT_NOT_EXIST.getRespCode(), RespCodeEnum.PUB_CERT_NOT_EXIST.getRespDesc());
            }
        }
        WechatSignService wechatSignService = new WechatSignServiceImpl();
        /*身份信息加密*/
        IdentificationInfo identificationInfo = riskMchntQueryRequest.getIdentificationInfo();
        if (identificationInfo != null) {
            if (identificationInfo.getCertNo() != null) {
                identificationInfo.setCertNo(wechatSignService.rsaEncSensitiveInfo(identificationInfo.getCertNo(), certInfo, "OAEPWITHSHA-1ANDMGF1PADDING"));
            }
            if (identificationInfo.getName() != null) {
                identificationInfo.setName(wechatSignService.rsaEncSensitiveInfo(identificationInfo.getName(), certInfo, "OAEPWITHSHA-1ANDMGF1PADDING"));
            }
            ConvertUtil.convertInput(identificationInfo);
        }
        /*公司信息*/
        LicenceInfo licenceInfo = riskMchntQueryRequest.getLicenceInfo();
        if (licenceInfo != null) {
            if (licenceInfo.getLicenceNo() != null) {
                licenceInfo.setLicenceNo(wechatSignService.rsaEncSensitiveInfo(licenceInfo.getLicenceNo(), certInfo, "OAEPWITHSHA-1ANDMGF1PADDING"));
            }
            ConvertUtil.convertInput(licenceInfo);
        }
        /*结算信息*/
        SettleAcctInfo settleAcctInfo = riskMchntQueryRequest.getSettleAcctInfo();
        if (settleAcctInfo != null) {
            if (settleAcctInfo.getSettleAcctNo() != null) {
                settleAcctInfo.setSettleAcctNo(wechatSignService.rsaEncSensitiveInfo(settleAcctInfo.getSettleAcctNo(), certInfo, "OAEPWITHSHA-1ANDMGF1PADDING"));
            }
            if (settleAcctInfo.getSettleAcctName() != null) {
                settleAcctInfo.setSettleAcctName(wechatSignService.rsaEncSensitiveInfo(settleAcctInfo.getSettleAcctName(), certInfo, "OAEPWITHSHA-1ANDMGF1PADDING"));
            }
            ConvertUtil.convertInput(settleAcctInfo);
        }
    }

    private CertInfo getRSAPubKey(SecPlanInfo secPlanInfo) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        DstChannelInfo dstChannelInfo = (DstChannelInfo) xmlTreeUtil.getXmlTreeObjectValue(WechatTreeNodeConstant.DST_CHANNEL_INFO, DstChannelInfo.class);
        DstChannelCfg dstChannelCfg = (DstChannelCfg) xmlTreeUtil.getXmlTreeObjectValue(WechatTreeNodeConstant.DST_CHANNEL_CFG, DstChannelCfg.class);
        xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.TXN_NUM, "getpubkey");
        SysParamService sysParamService = new SysParamServiceImpl();
        SysParam sysParam = sysParamService.getSysParamInfo(AppParamConstant.DEFAULT_INSTID,
                WechatConfigConstant.WECHAT_CONF_NODE, WechatConfigConstant.WECHAT_GET_PUBLIC_KEY_URL);
        if (sysParam == null) {
            rglog.error("获取设置支付目录url失败，请配置");
            throw new BizException(RespCodeEnum.CONFIG_PARAM_NOT_EXIST.getRespCode(), RespCodeEnum.CONFIG_PARAM_NOT_EXIST.getRespDesc());
        }
        xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.DEFINE_HTTP_URL_NODE, sysParam.getParamValue().replace("{merId}", dstChannelCfg.getProviderMchntNo()));
        int iReturnCode = schedule.callService(dstChannelInfo.getDstServiceName(), 0);
        if (iReturnCode != 0) {
            rglog.error("调用微信服<{}>失败，iReturnCode:<{}>", dstChannelInfo.getDstServiceName(), iReturnCode);
            throw new BizException(RespCodeEnum.PUB_CERT_NOT_EXIST.getRespCode(), RespCodeEnum.PUB_CERT_NOT_EXIST.getRespDesc());
        }
        String resultCode = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.WX_RESULT_CODE);
        String returnCode = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.WX_RETURN_CODE);
        String returnMsg = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.WX_RETURN_MSG);
        String errCode = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.WX_ERR_CODE);
        String errMsg = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.WX_ERR_MSG);
        if (!WechatParamConstant.STRING_SUCCESS.equals(resultCode)) {
            rglog.error("调用第三方服务失败,respCode=[{}],respDesc=[{}]", (null == errCode) ? returnCode : errCode, (null == errMsg) ? returnMsg : errMsg);
            throw new BizException(RespCodeEnum.PUB_CERT_NOT_EXIST.getRespCode(), RespCodeEnum.PUB_CERT_NOT_EXIST.getRespDesc());
        }
        xmlTreeUtil.deleteXmlTreeNode(WechatTreeNodeConstant.WX_RESULT_CODE);
        xmlTreeUtil.deleteXmlTreeNode(WechatTreeNodeConstant.WX_RETURN_CODE);
        xmlTreeUtil.deleteXmlTreeNode(WechatTreeNodeConstant.WX_RETURN_MSG);
        xmlTreeUtil.deleteXmlTreeNode(WechatTreeNodeConstant.WX_ERR_CODE);
        xmlTreeUtil.deleteXmlTreeNode(WechatTreeNodeConstant.WX_ERR_MSG);
        String pubKey = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.PUB_KEY);
        rglog.error("pubKey=[{}]", pubKey);
        String base64PubKey = getPublicKeyFromPemStr(pubKey);
        if (base64PubKey == null) {
            throw new BizException(RespCodeEnum.PUB_CERT_NOT_EXIST.getRespCode(), RespCodeEnum.PUB_CERT_NOT_EXIST.getRespDesc());
        }
        CertInfo certInfo = new CertInfo();
        certInfo.setCertIndex(secPlanInfo.getInstId() + secPlanInfo.getSecPlanId() + WechatParamConstant.ENCRYPT_CERT_TYPE);
        certInfo.setAlgorithmType("RSA");
        certInfo.setCertId(" ");
        certInfo.setCertExpiryDate("20991231");
        certInfo.setCertSaveType("0");
        certInfo.setCertValue(base64PubKey);
        certInfo.setKeyStatus(AppParamConstant.YES);
        certInfo.setCreateTime(DateUtil.getCurrentDateTime("yyyy-MM-dd HH:mm:ss.SSS"));
        CertInfoMapper certInfoMapper = new CertInfoMapperImpl();
        iReturnCode = certInfoMapper.insertCertInfo(certInfo);
        if (iReturnCode != 0) {
            rglog.error("保存证书失败");
            return null;
        }
        return certInfo;
    }

    private String getPublicKeyFromPemStr(String pemKeyString) throws Exception {
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(pemKeyString.getBytes("ISO-8859-1"))));
        StringBuilder stringBuilder = new StringBuilder();
        String readLine;
        while ((readLine = bufferedReader.readLine()) != null) {
            if (readLine.charAt(0) == '-') {
                continue;
            } else {
                stringBuilder.append(readLine);
            }
        }
        RSAPublicKey rsaPublicKey = RSAPublicKey.getInstance(Base64.decodeBase64(stringBuilder.toString()));
        RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(rsaPublicKey.getModulus(), rsaPublicKey.getPublicExponent());
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
        return Base64.encodeBase64String(publicKey.getEncoded());
    }
}
